/*
 * @Date: 2023-12-28 15:33:48
 * @LastEditors: lyx
 * @LastEditTime: 2024-01-25 16:36:17
 * @FilePath: \mt-middleware-server\src\feishu\feishu.service.ts
 * @Description:
 */
import { Inject, Injectable } from '@nestjs/common';
import { CreateFeishuDto } from './dto/create-feishu.dto';
import { UpdateFeishuDto } from './dto/update-feishu.dto';
import { CreateTenantAccessToken } from './dto/create-tenant-access-token.dto';
import { InjectModel } from 'nestjs-typegoose';
import { Feishu_Tenant as FeishuTenantSchema } from './entities/feishu_tenant.entity';
import { ReturnModelType } from '@typegoose/typegoose';
import { isExpiredToUpdatedAtISOS } from 'utils/common';
import { QueryFeiShuTenantAccessTokenType } from './dto/query-feishu-tenant-access-token.dto';
import { HttpService } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
import { firstValueFrom } from 'rxjs';
import { GetBatchOpenIdByPhoneDto } from './dto/get-batch-openId-by-phone.dto';
import {
  SendCardMessagesToUserDto,
  SendInteractiveMessagesDto,
  SendMessagesDto,
  SendMessagesToUserDto,
} from './dto/send-messages-to-user.dto';
import { h5_temp_test_content, temp_test_content } from './store';
import axios from 'axios';

@Injectable()
export class FeishuService {
  @Inject(ConfigService)
  private configService: ConfigService;

  constructor(
    @InjectModel(FeishuTenantSchema)
    private readonly tenantModel: ReturnModelType<typeof FeishuTenantSchema>,
    private readonly httpService: HttpService,
  ) {}

  async obpSyncOpenid() {
    const observable = this.httpService.post(
      `http://chenshoufeng:30000/sup-adapter/feishu/sync/openid`,
    );
    const res = await firstValueFrom(observable);
    console.log('obpSyncOpenid', res.data);
    return res.data;
  }

  async getHeadersRequestAccessToken(tenant: QueryFeiShuTenantAccessTokenType) {
    // 获取租户访问令牌
    const tenant_access_token_res = await this.getTenantAccessTokenFromDB(
      tenant,
    );
    const { accessToken = '' } = tenant_access_token_res;
    return accessToken;
  }
  /**
   * 从数据库获取租户访问令牌
   *
   * @param appId 应用ID
   * @returns 返回租户访问令牌
   */
  async getTenantAccessTokenFromDB(tenant: QueryFeiShuTenantAccessTokenType) {
    const appId = this.configService.get(`feishu.app.tenant.${tenant}.appId`);
    const res = await this.tenantModel.findOne({ appId: appId });
    console.log('从数据库获取 租户访问令牌 res', res);
    if (res?._id) {
      // 有数据
      // 获取到之后，判断是否过期
      const { updatedAt, expire } = res as any;
      // 获取当前时间
      const now = new Date().toISOString();

      const isExpire = isExpiredToUpdatedAtISOS(now, updatedAt, expire, 60);
      console.log('isExpire', isExpire);
      if (isExpire) {
        // 调用接口获取并且更新
        return this.getTenantAccessTokenFromOrigin(tenant);
      } else {
        console.log('返回的 token 对象', res);
        return res;
      }
    } else {
      // 没有数据：需要调用接口获取
      return this.getTenantAccessTokenFromOrigin(tenant);
    }
  }

  /**
   * 将租户访问令牌添加到数据库中
   *
   * @param createDto 创建租户访问令牌的DTO对象
   * @returns 返回创建的租户访问令牌对象
   */
  async addTenantAccessTokenToDb(createDto: CreateTenantAccessToken) {
    const { appId, ...rest } = createDto;
    // 删除之前的数据，防止数据重复
    await this.tenantModel.deleteMany({
      appId,
    });

    return await this.tenantModel.create(createDto);
  }

  /**
   * 从原始来源获取租户访问令牌
   *
   * @param tenant 租户
   * @returns 返回获取到的租户访问令牌
   */
  async getTenantAccessTokenFromOrigin(
    tenant: QueryFeiShuTenantAccessTokenType,
  ) {
    const observable = this.httpService.post(
      `${this.configService.get(
        'feishu.api.origin',
      )}/open-apis/auth/v3/tenant_access_token/internal`,
      {
        app_id: this.configService.get(`feishu.app.tenant.${tenant}.appId`),
        app_secret: this.configService.get(
          `feishu.app.tenant.${tenant}.appSecret`,
        ),
      },
    );
    const res = await firstValueFrom(observable);
    const {
      data,
      data: { code, expire, msg, tenant_access_token },
    } = res;
    console.log('getTenantAccessTokenFromOrigin data', data);
    if (code === 0) {
      const dto = {
        appId: this.configService.get(`feishu.app.tenant.${tenant}.appId`),
        accessToken: tenant_access_token,
        expire: expire,
      };
      const data = await this.addTenantAccessTokenToDb(dto);
      console.log('从原始来源获取租户访问令牌 data', data);
      return data;
    } else {
      return data;
    }
  }

  /**
   * 通过手机号批量获取 OpenId
   *
   * @param dto 包含租户信息和其它信息的对象
   * @returns 返回响应数据
   */
  async getBatchOpenIdByPhone(dto: GetBatchOpenIdByPhoneDto) {
    const { tenant, ...rest } = dto;

    const accessToken = await this.getHeadersRequestAccessToken(tenant);
    console.log('accessToken', accessToken);
    const headersRequest = {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${accessToken}`,
    };
    console.log('getBatchOpenIdByPhone accessToken', accessToken);
    const observable = this.httpService.post(
      `${this.configService.get(
        'feishu.api.origin',
      )}/open-apis/contact/v3/users/batch_get_id?user_id_type=open_id`,
      {
        ...rest,
      },
      {
        headers: headersRequest,
      },
    );
    const res = await firstValueFrom(observable);
    console.log('res?.data', res?.data);
    return res?.data;
  }

  /**
   * 向用户发送消息
   *
   * @param dto 发送消息的数据传输对象
   * @returns 返回发送消息的结果
   */
  getTextMessagesToUser(dto: SendMessagesToUserDto) {
    const { content } = dto as SendMessagesToUserDto;
    return {
      ...dto,
      content: JSON.stringify({ text: content }),
    } as SendMessagesToUserDto;
  }

  getCardMiniProgramMessagesDto(dto: SendInteractiveMessagesDto) {
    const { url, appId } = dto;
    return {
      ...dto,
      content: JSON.stringify(temp_test_content(url, appId)),
    } as SendCardMessagesToUserDto;
  }
  getCardH5MessagesDto(dto: SendInteractiveMessagesDto) {
    const { url, appId } = dto;
    return {
      ...dto,
      content: JSON.stringify(h5_temp_test_content(url, appId)),
    } as SendCardMessagesToUserDto;
  }

  async sendMessagesToUser(dto: SendMessagesDto | SendInteractiveMessagesDto) {
    const {
      tenant,
      receivePhone,
      receive_id,
      msg_type = 'text',
      program_type = 'mini',
      ...rest
    } = dto;
    // 获取租户访问令牌
    const accessToken = await this.getHeadersRequestAccessToken(tenant);
    console.log('accessToken', accessToken);
    const headersRequest = {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${accessToken}`,
    };
    let receive_openId = receive_id;
    if (receivePhone) {
      // ! 需要自己查询 openId
      const receiveData = await this.getBatchOpenIdByPhone({
        tenant,
        mobiles: [receivePhone],
      });
      console.log('receiveData', receiveData);
      const {
        code,
        data,
        data: { user_list = [] },
      } = receiveData;
      if (code !== 0) {
        return receiveData;
      }
      receive_openId = user_list.find(
        (item) => item.mobile === receivePhone,
      )?.user_id;
    }

    let cDto: any = {
      ...rest,
      msg_type,
      receive_id: receive_openId,
    };
    if (msg_type === 'text') {
      cDto = this.getTextMessagesToUser(cDto);
    } else if (msg_type === 'interactive') {
      if (program_type === 'h5') {
        cDto = this.getCardH5MessagesDto(cDto);
      } else if (program_type === 'mini') {
        cDto = this.getCardMiniProgramMessagesDto(cDto);
      }
    }
    console.log('cDto', cDto);

    const observable = this.httpService.post(
      `${this.configService.get(
        'feishu.api.origin',
      )}/open-apis/im/v1/messages?receive_id_type=open_id`,
      {
        ...cDto,
      },
      {
        headers: headersRequest,
      },
    );
    const res = await firstValueFrom(observable);
    console.log('res?.data', res?.data);
    return res?.data;
  }
}
